package com.mldong.aop;

import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.json.JSONUtil;
import com.mldong.annotation.ResubmitLimit;
import com.mldong.context.constant.ConstantContextHolder;
import com.mldong.exception.ServiceException;
import com.mldong.util.HttpServletUtil;
import com.mldong.util.RedisUtil;
import com.mldong.web.LoginUserHolder;
import com.mldong.web.QueryParamHolder;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 *  重复提交限制
 * @author mldong
 * @date 2022/6/2
 */
@Aspect
@Order(110)
@Component
public class ResubmitLimitAop {
    /**
     * 重复提交切入点
     *
     */
    @Pointcut("@annotation(com.mldong.annotation.ResubmitLimit) || execution(* com.mldong.modules..controller..save*(..))")
    private void getResubmitLimitPointCut() {
    }
    /**
     * 重复提交切入点
     *
     */
    @Before(value = "getResubmitLimitPointCut()")
    public void doResubmitLimit(JoinPoint joinPoint) {
        // 获取方法对象
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        ResubmitLimit resubmitLimit = method.getAnnotation(ResubmitLimit.class);
        if(resubmitLimit == null && !ConstantContextHolder.getSysConfigWithDefault("RESUBMIT_LIMIT_OPEN",Boolean.class,false)){
            return;
        }
        String signature = SecureUtil.md5(JSONUtil.toJsonStr(QueryParamHolder.me()));
        // 自动生成 name（如果未指定）
        String targetClass = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();

        int defaultTimeout = ConstantContextHolder.getSysConfigWithDefault("RESUBMIT_LIMIT_TIMEOUT",Integer.class,5);
        if(resubmitLimit != null) {
            defaultTimeout = resubmitLimit.autoReleaseTime();
        }
        String effectiveName = "";
        TimeUnit timeUnit = resubmitLimit != null ? resubmitLimit.timeUnit() : TimeUnit.SECONDS;
        if(resubmitLimit == null || StrUtil.isBlank(resubmitLimit.name())) {
            effectiveName = targetClass + "." + methodName;
        } else {
            effectiveName = resubmitLimit.name();
        }
        String userName = "";
        if(LoginUserHolder.me() == null){
            userName = SecureUtil.md5(HttpServletUtil.getClientIP()+HttpServletUtil.getClientBrowser());
        } else {
            userName = LoginUserHolder.getUserName();
        }
        String lockKey = String.format("resubmit_limit:%s:user_%s:params_%s",
                effectiveName,
                userName,
                signature);
        // 同一个接口、同一个人、相同参数，在某一时间范围内，定义为重复提交
        boolean enable = RedisUtil.lock(lockKey, StrUtil.uuid(), defaultTimeout, timeUnit);
        if(!enable) {
            throw new ServiceException(99999999, "不可重复提交");
        }
    }
}
